/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra.wsrm.impl;

import java.math.BigInteger;
import java.util.Calendar;

import cgl.narada.wsinfra.schemas.utility.ExpiresDocument;
import cgl.narada.wsinfra.schemas.wsrm.AckRequestedDocument;
import cgl.narada.wsinfra.schemas.wsrm.AckRequestedType;
import cgl.narada.wsinfra.schemas.wsrm.HandshakeType;
import cgl.narada.wsinfra.schemas.wsrm.SequenceAcknowledgementDocument;
import cgl.narada.wsinfra.schemas.wsrm.SequenceDocument;
import cgl.narada.wsinfra.schemas.wsrm.SequenceType;
import cgl.narada.wsinfra.schemas.wsrm.TerminateSequenceDocument;
import cgl.narada.wsinfra.util.XmlContentTransfer;
import cgl.narada.wsinfra.util.XmlDateTimeConversion;
import cgl.narada.wsinfra.wsrm.WsrmAckOperations;
import cgl.narada.wsinfra.wsrm.WsrmElementCreation;
import cgl.narada.wsinfra.wsrm.WsrmProcessingFactory;

/** This is a class which facilitates the creation of WSRM elements that
    are added to exchanges.

    @author Shrideep Pallickara
    $Date: 2005/07/29 22:41:30 $
    $Revision: 1.4 $
*/

public class WsrmElementCreationImpl implements WsrmElementCreation {
  private static final WsrmElementCreation instance = new WsrmElementCreationImpl();

  private WsrmAckOperations wsrmAckOperations;
  private XmlDateTimeConversion xmlDateTimeConversion;
  private XmlContentTransfer xmlContentTransfer;

  private String moduleName = "WsrmElementCreation: ";

  private WsrmElementCreationImpl() {
    wsrmAckOperations = WsrmProcessingFactory.getWsrmAckOperations();
    xmlDateTimeConversion = XmlDateTimeConversion.getInstance();
    xmlContentTransfer = XmlContentTransfer.getInstance();
  }

  public static WsrmElementCreation getInstance() {
    return instance;
  }


  /** Creates a sequence document based on the specified parameters */
  public SequenceDocument newSequence(String identifier, long messageNumber) {
    SequenceDocument sequenceDocument = SequenceDocument.Factory.newInstance();
    SequenceType sequenceType = sequenceDocument.addNewSequence();

    sequenceType.addNewIdentifier().setStringValue(identifier);

    BigInteger bigMessageNumber  = convertLongToBigInteger(messageNumber);
    sequenceType.setMessageNumber(bigMessageNumber);

    return sequenceDocument;
  }
  
  
  /** Creates a sequence document based on the specified parameters */
  public SequenceDocument 
  newSequence(String identifier, long messageNumber,
	      boolean lastMessage, Calendar expiresAt) {
    SequenceDocument sequenceDocument = newSequence(identifier, messageNumber);
    SequenceType sequenceType = sequenceDocument.getSequence();
    
    if (lastMessage) {
      sequenceType.addNewLastMessage();
    }
    
    if (expiresAt != null) {
      ExpiresDocument expires = xmlDateTimeConversion.getExpires(expiresAt);
      xmlContentTransfer.copyFromSourceToDestinationAsLastChild(expires,
								sequenceType);
    }
    return sequenceDocument;
  }
  
  
  /** Creates an AckRequested document based on the specified parameters */
  public AckRequestedDocument newAckRequested(String identifier) {
    AckRequestedDocument ackRequestedDocument =
      AckRequestedDocument.Factory.newInstance();
    AckRequestedType ackRequestedType = 
      ackRequestedDocument.addNewAckRequested();
    
    ackRequestedType.addNewIdentifier().setStringValue(identifier);
    
    return ackRequestedDocument;
  }
  
  
  /** Creates an AckRequested document based on the specified parameters */
  public AckRequestedDocument newAckRequested(String identifier, 
					      long maxMessageNumberUsed) {
    AckRequestedDocument ackRequestedDocument = newAckRequested(identifier);
    AckRequestedType ackRequestedType=ackRequestedDocument.getAckRequested();
    
    BigInteger bigMaxMessageNumber = 
      convertLongToBigInteger(maxMessageNumberUsed);
    ackRequestedType.setMaxMessageNumberUsed(bigMaxMessageNumber);

    return ackRequestedDocument;
  }
  
  
  
  /** Creates a SequenceAcknowledgement element based on the specified
      parameters. If <i>postiveAcks</i> is true, then the SequenceACK contains
      positive acknowledgements otherwise it contains negative 
      acknowledgements. The specification forbids inclusion of both 
      positive and negative acknowledgements. */
  public SequenceAcknowledgementDocument 
  newSequenceAcknowledgement(String identifier, long[] acknowledgementsArray,
			     boolean positiveAcks) {

    SequenceAcknowledgementDocument sequenceAckDocument =
      SequenceAcknowledgementDocument.Factory.newInstance();
    SequenceAcknowledgementDocument.SequenceAcknowledgement  sequenceAck =
      sequenceAckDocument.addNewSequenceAcknowledgement();

    sequenceAck.addNewIdentifier().setStringValue(identifier);
    
    /** Process if it is negative acknowledgement */
    if (!positiveAcks) {
      BigInteger[] nackArray = 
	convertLongtoBigIntegerArray(acknowledgementsArray);
      sequenceAck.setNackArray(nackArray);
      return sequenceAckDocument;
    }

    /** Process positive acknowledgements */
    SequenceAcknowledgementDocument.SequenceAcknowledgement.AcknowledgementRange[] acknowledgementRanges = 
      wsrmAckOperations.getAcknowledgementRanges(acknowledgementsArray);
    
    
    sequenceAck.setAcknowledgementRangeArray(acknowledgementRanges);

    return sequenceAckDocument;
  }
  
  
  
  /** Terminates a sequence identified the specified identifier */
  public TerminateSequenceDocument newTerminateSequence(String identifier) {
    TerminateSequenceDocument terminateSequenceDocument =
      TerminateSequenceDocument.Factory.newInstance();
    HandshakeType terminateSequence = 
      terminateSequenceDocument.addNewTerminateSequence();
    
    terminateSequence.addNewIdentifier().setStringValue(identifier);
    
    return terminateSequenceDocument;
  }


  /** Create a new acknowledgement range based on the specified parameters */
  public SequenceAcknowledgementDocument.SequenceAcknowledgement.AcknowledgementRange
  newAcknowledgementRange(long upperMessageNumber, long lowerMessageNumber) {
    SequenceAcknowledgementDocument.SequenceAcknowledgement.AcknowledgementRange acknowledgementRange = 
      SequenceAcknowledgementDocument.SequenceAcknowledgement.AcknowledgementRange.Factory.newInstance();

    BigInteger bigUpperMessageNumber = 
      convertLongToBigInteger(upperMessageNumber);
    BigInteger bigLowerMessageNumber = 
      convertLongToBigInteger(lowerMessageNumber);

    acknowledgementRange.setUpper(bigUpperMessageNumber);
    acknowledgementRange.setLower(bigLowerMessageNumber);

    return acknowledgementRange;
  }

  /** Converts a long into a BigInteger. This is the method which all such
      conversions should call. This way, if there is a more optimized way to
      do our conversions we just need to change it here and it impact every
      place. I am not very happy with this conversion, it is perhaps a little 
      inelegant */
  private BigInteger convertLongToBigInteger(long longValue) {
    String longString = ""+ longValue;
    BigInteger bigInteger = new BigInteger(longString);
    return bigInteger;
  }

  
  /** Converts an array of long into a Big Integer array */
  private BigInteger[] convertLongtoBigIntegerArray(long[] longValues) {
    int sizeOfArray = longValues.length;
    BigInteger[] bigValues = new BigInteger[sizeOfArray];
    
    for (int i=0; i < sizeOfArray; i++) {
      bigValues[i] = convertLongToBigInteger(longValues[i]);
    }

    return bigValues;
  }


}



